﻿using System;
using System.Linq;
using System.Linq.Expressions;
using MathService.MathParser.ExpressionTrees.Nodes;

namespace MathService.MathParser
{
    /// <summary>Комплексный оператор интегрирования</summary>
    public class IntegralOperator : ComplexOperatorItem
    {
        //Использовать адаптивный метод интегрирования
        private bool f_IsAdaptive;

        /// <summary>Создание нового комплексного интегритора интегрирования</summary>
        public IntegralOperator() : this("∫") { }

        /// <summary>Создание нового комплексного интегритора интегрирования</summary>
        /// <param name="Name">Имя оператора</param>
        public IntegralOperator(string Name) : base(Name) { }

        /// <summary>Инициализация оператора</summary>
        /// <param name="Parameters">Блок параметров</param>
        /// <param name="Function">Блок ядра функции</param>
        /// <param name="Parser">Парсер</param>
        /// <param name="Expression">Внешнее выражение</param>
        public override void Initialize(MathExpression Parameters, MathExpression Function, ExpressionParser Parser, MathExpression Expression)
        {
            base.Initialize(Parameters, Function, Parser, Expression);

            var iterator_var = Parameters.Tree
                        .Where(n => n is VariableValueNode && n.Parent is EqualityOperatorNode)
                        .Select(n => ((VariableValueNode)n).Variable)
                        .FirstOrDefault();
            if(iterator_var == null)
                throw new FormatException();
            var iterator_var_name = iterator_var.Name;
            var iterator_diff_var = Parameters.Tree
                        .Where(n => n is VariableValueNode && n.Parent is EqualityOperatorNode
                            && ((VariableValueNode)n).Variable.Name == string.Format("d{0}", iterator_var_name))
                        .Select(n => ((VariableValueNode)n).Variable)
                        .FirstOrDefault();
            f_IsAdaptive = iterator_diff_var == null;

            Function.Variable.ClearCollection();
            Function.Variable.Add(iterator_var);
            Function.Tree
                .Where(n => n is VariableValueNode)
                .Cast<VariableValueNode>()
                .Where(n => !ReferenceEquals(n.Variable, iterator_var))
                .Foreach(n => Function.Variable.Add(n.Variable = Expression.Variable[n.Variable.Name]));

            Parameters.Variable.ClearCollection();
            Parameters.Variable.Add(iterator_var);
            if(!f_IsAdaptive) Parameters.Variable.Add(iterator_diff_var);
            Parameters.Tree
                .Where(n => n is VariableValueNode)
                .Cast<VariableValueNode>()
                .Where(n => !ReferenceEquals(n.Variable, iterator_var) && !ReferenceEquals(n.Variable, iterator_diff_var))
                .Foreach(n => Parameters.Variable.Add(n.Variable = Expression.Variable[n.Variable.Name]));

        }

        /// <summary>Метод определения значения</summary>
        /// <returns>Численное значение элемента выражения</returns>
        public override double GetValue(MathExpression ParametersExpression, MathExpression Function)
        {
            var x = ParametersExpression.Variable[0];
            var x_node = ParametersExpression.Tree
                        .Where(n => n is VariableValueNode)
                        .Cast<VariableValueNode>()
                        .First(n => ReferenceEquals(x, n.Variable));
            var interval = (IntervalNode)x_node.Parent.Right;
            var min = ((ComputedNode)interval.Left).Compute();
            var max = ((ComputedNode)interval.Right).Compute();

            Func<double, double> f = xx =>
            {
                x.Value = xx;
                return Function.Compute();
            };

            if(f_IsAdaptive)
                return f.GetIntegralValue_Adaptive(min, max);

            var dx = ParametersExpression.Variable[1];
            var dx_node = ParametersExpression.Tree
                        .Where(n => n is VariableValueNode)
                        .Cast<VariableValueNode>()
                        .First(n => ReferenceEquals(dx, n.Variable));
            dx.Value = ((ConstValueNode)dx_node.Parent.Right).Value;
            return f.GetIntegralValue(min, max, dx.GetValue());
        }

        private delegate double AdaptiveIntegralDelegate(Delegate d, double Min, double Max, double[] Parameters);
        private static double GetAdaptiveIntegral(Delegate d, double Min, double Max, double[] Parameters)
        {
            var pp_len = Parameters.Length;

            var xx = new object[pp_len + 1];
            Array.Copy(Parameters, 0, xx, 1, pp_len);
            Func<double, double> f = x =>
            {
                xx[0] = x;
                return (double)d.DynamicInvoke(xx);
            };
            return f.GetIntegralValue_Adaptive(Min, Max);
        }

        private delegate double IntegralDelegate(Delegate d, double Min, double Max, double[] Parameters, double dx);
        private static double GetIntegral(Delegate d, double Min, double Max, double[] Parameters, double dx)
        {
            var pp_len = Parameters.Length;

            var xx = new object[pp_len + 1];
            Array.Copy(Parameters, 0, xx, 1, pp_len);
            Func<double, double> f = x =>
            {
                xx[0] = x;
                return (double)d.DynamicInvoke(xx);
            };
            return f.GetIntegralValue(Min, Max, dx);
        }

        /// <summary>Скомпилировать в выражение</summary>
        /// <returns>Скомпилированное выражение System.Linq.Expressions</returns>
        public override Expression Compile(MathExpression ParametersExpression, MathExpression Function)
        {
            var iterator = ParametersExpression.Variable[0];
            var x_node = ParametersExpression.Tree
                        .Where(n => n is VariableValueNode)
                        .Cast<VariableValueNode>()
                        .First(n => ReferenceEquals(iterator, n.Variable));

            var interval = (IntervalNode)x_node.Parent.Right;
            var min = ((ComputedNode)interval.Left).Compile();
            var max = ((ComputedNode)interval.Right).Compile();

            var iterator_parameter = Expression.Parameter(typeof(double), iterator.Name);
            var f_parameters = new[] { iterator_parameter };
            var f_body = ((ComputedNode)Function.Tree.Root).Compile(f_parameters);
            var f_expr = Expression.Lambda(f_body, f_parameters).Compile();

            if(f_IsAdaptive)
            {
                var expr_p = new[]
                {
                    Expression.Constant(f_expr), min, max,
                    Expression.NewArrayInit(typeof(double), new Expression[0])
                };
                return Expression.Call(new AdaptiveIntegralDelegate(GetAdaptiveIntegral).Method, expr_p);
            }

            var dx_var = ParametersExpression.Variable[1];
            var dx_node = ParametersExpression.Tree
                        .Where(n => n is VariableValueNode)
                        .Cast<VariableValueNode>()
                        .First(n => ReferenceEquals(dx_var, n.Variable));
            var dx = dx_node.Compile();

            var expr_p_adapt = new[]
            {
                Expression.Constant(f_expr), min, max,
                Expression.NewArrayInit(typeof(double), new Expression[0]),
                dx
            };

            return Expression.Call(new IntegralDelegate(GetIntegral).Method, expr_p_adapt);
        }

        /// <summary>Скомпилировать в выражение</summary>
        /// <param name="Function">Ядро функции</param>
        /// <param name="Parameters">Массив параметров</param>
        /// <param name="ParametersExpression">Выражение параметров</param>
        /// <returns>Скомпилированное выражение System.Linq.Expressions</returns>
        public override Expression Compile(MathExpression ParametersExpression, MathExpression Function, ParameterExpression[] Parameters)
        {
            var iterator = ParametersExpression.Variable[0];
            var x_node = ParametersExpression.Tree
                        .Where(n => n is VariableValueNode)
                        .Cast<VariableValueNode>()
                        .First(n => ReferenceEquals(iterator, n.Variable));

            var interval = (IntervalNode)x_node.Parent.Right;
            var min = ((ComputedNode)interval.Left).Compile(Parameters);
            var max = ((ComputedNode)interval.Right).Compile(Parameters);

            var iterator_parameter = Expression.Parameter(typeof(double), iterator.Name);
            var f_parameters = new[] { iterator_parameter };
            var f_body = ((ComputedNode)Function.Tree.Root).Compile(f_parameters);
            var f_expr = Expression.Lambda(f_body, f_parameters).Compile();

            if(f_IsAdaptive)
            {
                var expr_p = new[]
                {
                    Expression.Constant(f_expr), min, max,
                    Expression.NewArrayInit(typeof(double), new Expression[0])
                };
                return Expression.Call(new AdaptiveIntegralDelegate(GetAdaptiveIntegral).Method, expr_p);
            }

            var dx_var = ParametersExpression.Variable[1];
            var dx_node = ParametersExpression.Tree
                        .Where(n => n is VariableValueNode)
                        .Cast<VariableValueNode>()
                        .First(n => ReferenceEquals(dx_var, n.Variable));
            var dx = dx_node.Compile();

            var expr_p_adapt = new[]
            {
                Expression.Constant(f_expr), min, max,
                Expression.NewArrayInit(typeof(double), Parameters),
                dx
            };

            return Expression.Call(new IntegralDelegate(GetIntegral).Method, expr_p_adapt);
        }
    }
}